package com.ai.plug.core.provider;

import com.ai.plug.core.annotation.McpResource;
import com.ai.plug.core.spec.callback.resource.AsyncMcpResourceMethodCallback;
import com.ai.plug.core.spec.callback.resource.SyncMcpResourceMethodCallback;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.util.Assert;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author han
 * @time 2025/6/19 17:48
 */

public class McpResourceProvider {
    /**
     * 存储资源对象的列表，这些对象包含被 @McpResource 注解标记的方法
     */
    private final List<Object> resourceObjects;

    /**
     * 构造函数，初始化资源对象列表
     *
     * @param resourceObjects 资源对象列表，不能为空
     */
    public McpResourceProvider(List<Object> resourceObjects) {
        // 检查 resourceObjects 是否为 null，如果为 null 则抛出异常
        Assert.notNull(resourceObjects, "resourceObjects cannot be null");
        // 初始化 resourceObjects 属性
        this.resourceObjects = resourceObjects;
    }

    /**
     * 获取异步资源规范列表，用于处理异步资源请求
     *
     * @return 异步资源规范列表
     */
    public List<McpServerFeatures.AsyncResourceSpecification> getAsyncResourceSpecifications() {
        // 通过 resourceObjects 流式处理每个资源对象
        List<McpServerFeatures.AsyncResourceSpecification> methodCallbacks = this.resourceObjects.stream()
                // 对每个资源对象获取其方法列表
                .map(resourceObject -> Stream.of(doGetClassMethods(resourceObject))
                        // 过滤出带有 @McpResource 注解的方法
                        .filter(resourceMethod -> resourceMethod.isAnnotationPresent(McpResource.class))
                        // 将每个符合条件的方法转换为异步资源规范
                        .map(mcpResourceMethod -> {
                            // 获取方法上的 @McpResource 注解
                            McpResource resourceAnnotation = mcpResourceMethod.getAnnotation(McpResource.class);

                            // 提取资源的 URI
                            String uri = resourceAnnotation.uri();
                            // 获取资源名称，如果注解中未指定则使用方法名
                            String name = getName(mcpResourceMethod, resourceAnnotation);
                            // 获取资源标题，如果注解中未指定则使用方法名
                            String title = getTitle(mcpResourceMethod, resourceAnnotation);
                            // 获取资源描述
                            String description = resourceAnnotation.description();
                            // 获取资源的 MIME 类型
                            String mimeType = resourceAnnotation.mimeType();
                            // 创建 MCP 资源对象
                            var mcpResource = new McpSchema.Resource(uri, name, title, description, mimeType, null, null);

                            // 构建异步资源方法回调
                            AsyncMcpResourceMethodCallback methodCallback = AsyncMcpResourceMethodCallback.builder()
                                    // 设置回调方法
                                    .method(mcpResourceMethod)
                                    // 设置包含该方法的 bean 对象
                                    .bean(resourceObject)
                                    // 设置资源信息
                                    .resource(mcpResource)
                                    // 构建回调对象
                                    .build();

                            // 返回异步资源规范，包含资源信息和回调方法
                            return new McpServerFeatures.AsyncResourceSpecification(mcpResource, methodCallback);
                        })
                        // 将每个资源对象的处理结果转换为列表
                        .toList())
                // 将所有资源对象的处理结果展平为一个列表
                .flatMap(List::stream)
                // 最终收集为一个列表
                .toList();

        // 返回构建好的异步资源规范列表
        return methodCallbacks;
    }

    /**
     * 获取同步资源规范列表，用于处理同步资源请求
     *
     * @return 同步资源规范列表
     */
    public List<McpServerFeatures.SyncResourceSpecification> getSyncResourceSpecifications() {
        // 通过 resourceObjects 流式处理每个资源对象
        List<McpServerFeatures.SyncResourceSpecification> methodCallbacks = this.resourceObjects.stream()
                // 对每个资源对象获取其方法列表
                .map(resourceObject -> Stream.of(doGetClassMethods(resourceObject))
                        // 过滤出带有 @McpResource 注解的方法
                        .filter(resourceMethod -> resourceMethod.isAnnotationPresent(McpResource.class))
                        // 将每个符合条件的方法转换为同步资源规范
                        .map(mcpResourceMethod -> {
                            // 获取方法上的 @McpResource 注解
                            McpResource resourceAnnotation = mcpResourceMethod.getAnnotation(McpResource.class);

                            // 提取资源的 URI
                            String uri = resourceAnnotation.uri();
                            // 获取资源名称，如果注解中未指定则使用方法名
                            String name = getName(mcpResourceMethod, resourceAnnotation);
                            // 获取资源标题，如果注解中未指定则使用方法名
                            String title = getTitle(mcpResourceMethod, resourceAnnotation);
                            // 获取资源描述
                            String description = resourceAnnotation.description();
                            // 获取资源的 MIME 类型
                            String mimeType = resourceAnnotation.mimeType();
                            // 创建 MCP 资源对象
                            var mcpResource = new McpSchema.Resource(uri, name, title, description, mimeType, null, null);

                            // 构建同步资源方法回调
                            SyncMcpResourceMethodCallback methodCallback = SyncMcpResourceMethodCallback.builder()
                                    // 设置回调方法
                                    .method(mcpResourceMethod)
                                    // 设置包含该方法的 bean 对象
                                    .bean(resourceObject)
                                    // 设置资源信息
                                    .resource(mcpResource)
                                    // 构建回调对象
                                    .build();

                            // 返回同步资源规范，包含资源信息和回调方法
                            return new McpServerFeatures.SyncResourceSpecification(mcpResource, methodCallback);
                        })
                        // 将每个资源对象的处理结果转换为列表
                        .toList())
                // 将所有资源对象的处理结果展平为一个列表
                .flatMap(List::stream)
                // 最终收集为一个列表
                .toList();

        // 返回构建好的同步资源规范列表
        return methodCallbacks;
    }

    /**
     * 获取给定 bean 对象的所有声明方法，并按方法名和参数类型排序
     *
     * @param bean bean 对象
     * @return 排序后的方法数组
     */
    protected Method[] doGetClassMethods(Object bean) {
        // 获取 bean 类的所有声明方法
        Method[] methods = bean.getClass().getDeclaredMethods();
        // 使用方法名和参数类型对方法进行排序，确保一致性
        Arrays.sort(methods, Comparator
                .comparing(Method::getName)
                .thenComparing(method -> Arrays.toString(method.getParameterTypes())));
        // 返回排序后的方法数组
        return methods;
    }

    /**
     * 获取资源名称，如果注解中未指定则使用方法名
     *
     * @param method   资源方法
     * @param resource 资源注解
     * @return 资源名称
     */
    private static String getName(Method method, McpResource resource) {
        // 检查 method 是否为 null，如果为 null 则抛出异常
        Assert.notNull(method, "method cannot be null");
        // 如果 resource 为 null 或者 name 为空，则使用方法名作为资源名称
        if (resource == null || resource.name() == null || resource.name().isEmpty()) {
            return method.getName();
        }
        // 返回注解中指定的资源名称
        return resource.name();
    }

    /**
     * 获取资源标题，如果注解中未指定则使用方法名
     *
     * @param method   资源方法
     * @param resource 资源注解
     * @return 资源标题
     */
    private static String getTitle(Method method, McpResource resource) {
        // 检查 method 是否为 null，如果为 null 则抛出异常
        Assert.notNull(method, "method cannot be null");
        // 如果 resource 为 null 或者 name 为空，则使用方法名作为资源标题
        if (resource == null || resource.name() == null || resource.name().isEmpty()) {
            return method.getName();
        }
        // 返回注解中指定的资源标题
        return resource.title();
    }
}